import { writeFileSync } from 'fs';
import { join } from 'path';
import { terminal } from 'terminal-kit';
import { CloudBaseConfig, PackageWithCloudBase, ScfConfig, ScfConfigCommon, PackageWithScf } from './types';
import { convertPathSeparator, isDir, isFile, loadJsonObject, loadJsonObjectThrow } from './utils';

const pkgFilename = 'package.json';
const cloudbaseFilename = 'cloudbaserc.json';

function combineIgnoreArray(ignore?: string | string[], merge?: string | string[]): string[] {
  const clone = (ignore == null ? [] : !Array.isArray(ignore) ? [ignore] : ignore).slice();
  if (merge != null) {
    (!Array.isArray(merge) ? [merge] : merge).forEach(it => {
      if (!clone.includes(it)) {
        clone.push(it);
      }
    });
  }
  return clone;
}

export function useCloudBaseContext(workingPath: string) {
  if (!isDir(workingPath)) {
    throw new Error('Invalid working directory!');
  }

  const rootFiles = {
    pkg: join(workingPath, pkgFilename),
    cloudbase: join(workingPath, cloudbaseFilename),
  };

  if (!isFile(rootFiles.pkg)) {
    throw new Error('No package.json file found in working directory!');
  }

  const pkg = loadJsonObjectThrow<PackageWithCloudBase>(rootFiles.pkg);

  const _cloudbase = loadJsonObject<CloudBaseConfig>(rootFiles.cloudbase);

  const cloudbase = {
    ...{
      envId: '',
      region: undefined,
      functionRoot: './functions',
      functions: [],
    },
    ..._cloudbase,
    ...pkg.cloudbaseConfig,
  };

  if (cloudbase.functions == null || !Array.isArray(cloudbase.functions)) {
    cloudbase.functions = [];
  }

  return {
    pkg,
    cloudbase,
    rootFiles,
    get functionRoot(): string {
      return join(workingPath, cloudbase.functionRoot);
    },
    addScfProject(scfPath: string, writeFile = false) {
      if (!isDir(scfPath)) {
        throw new Error('Invalid cloud function directory!');
      }
      if (!isFile(join(scfPath, pkgFilename))) {
        throw new Error('No package.json file found in working directory!');
      }
      const scfPkg = loadJsonObject<PackageWithScf>(join(scfPath, pkgFilename));
      this.addScfConfig(scfPkg?.scf, writeFile);
    },
    addScfConfig(scfConfig?: ScfConfig, writeFile = false) {
      if (scfConfig == null) {
        throw new Error('Invalid scf config!');
      }
      if (scfConfig.name == null || !scfConfig.name) {
        throw new Error('Undefined scf name!');
      }
      const index = cloudbase.functions.findIndex(it => it.name === scfConfig.name);
      if (index < 0) {
        cloudbase.functions.push(scfConfig);
      } else {
        cloudbase.functions[index] = this.mergeScfConfig(cloudbase.functions[index], scfConfig);
      }

      if (writeFile) {
        terminal('Updating ').yellow(scfConfig.name);
        terminal(' config into ').cyan('cloudbaserc.json');
        try {
          writeFileSync(rootFiles.cloudbase, JSON.stringify(cloudbase, null, 2));
          terminal(' ... ').green('success');
        } catch (e) {
          terminal(' ... ').red('error');
        }

        process.stdout.write('\n');
      }

      return cloudbase;
    },
    mergeScfConfig(c1: ScfConfigCommon | ScfConfig, c2?: ScfConfigCommon | ScfConfig): ScfConfig {
      const basic = { name: '', ...c1 };
      if (c2 == null) return basic;
      const { ignore: basicIgnore, ...basicRest } = basic;
      const { ignore: mergeIgnore, ...merge } = c2;
      return {
        ...basicRest,
        ...merge,
        ignore: combineIgnoreArray(basicIgnore, mergeIgnore),
      };
    },
    addDeployScript(scfName: string, writeFile = false) {
      if (pkg.scripts == null) {
        pkg.scripts = {};
      }
      const matchApi = scfName.match(/^api-(.*)$/i);
      const httpPath = matchApi && matchApi[1] ? ` --path /${convertPathSeparator(matchApi[1])}` : '';
      const script = `tcb fn deploy ${scfName}${httpPath} --force`;
      const scriptKey = `deploy:${scfName}`;
      if (!pkg.scripts[scriptKey]) {
        pkg.scripts[scriptKey] = script;
        terminal('Add script ').yellow(scriptKey);
        terminal(' into ').cyan(' package.json\n');

        if (writeFile) {
          try {
            writeFileSync(rootFiles.pkg, JSON.stringify(pkg, null, 2));
            terminal('Update ').cyan('package.json ').green('success');
          } catch (e) {
            terminal('Update ').cyan('package.json ').red('error');
          }
          process.stdout.write('\n');
          process.stdout.write('\n');
        }
      } else {
        terminal('The script ').yellow(scriptKey);
        terminal(' existed, please adjust manually.\n').yellow(`"${scriptKey}": "${script}"\n\n`);
      }
    },
  };
}
